#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time   : 2021/7/13 21:15
# @Author : cjw
"""
公共工具函数
"""
import logging
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional

import emails
import jwt
from emails.template import JinjaTemplate
from jwt.exceptions import InvalidTokenError

from app.core.config import settings

# 密码重置信息
password_reset_jwt_subject = 'preset'


def send_email(email_to: str, subject_template='', html_template='', environment={}) -> None:
    """
    发送邮件
    :param email_to: 收件人信息
    :param subject_template: 邮件主题
    :param html_template: 邮件html内容模板
    :param environment: 发件人信息
    :return:
    """
    assert settings.EMAILS_ENABLED, '没有为电子邮件变量提供配置'

    # 邮件内容
    message = emails.Message(
        subject=JinjaTemplate(subject_template),
        html=JinjaTemplate(html_template),
        mail_from=(settings.EMAILS_FROM_NAME, settings.EMAILS_FROM_EMAIL),
    )

    # 邮件发送地址和端口
    smtp_options = {'host': settings.SMTP_HOST, 'port': settings.SMTP_PORT}

    # 邮件发送配置
    if settings.SMTP_TLS:
        smtp_options['tls'] = True
    if settings.SMTP_USER:
        smtp_options['user'] = settings.SMTP_USER
    if settings.SMTP_PASSWORD:
        smtp_options['password'] = settings.SMTP_PASSWORD

    # 发送邮件
    response = message.send(to=email_to, render=environment, smtp=smtp_options)
    logging.info(f'发送电子邮件结果: {response}')


def send_test_email(email_to: str) -> None:
    """
    发送邮件测试
    :param email_to: 收件人
    :return:
    """
    project_name = settings.PROJECT_NAME
    subject = f'{project_name} - 测试邮件'
    with open(Path(settings.EMAIL_TEMPLATES_DIR) / 'test_email.html') as f:
        template_str = f.read()
    send_email(
        email_to=email_to,
        subject_template=subject,
        html_template=template_str,
        environment={'project_name': settings.PROJECT_NAME, 'email': email_to},
    )


def send_reset_password_email(email_to: str, email: str, token: str) -> None:
    """
    发送密码重置邮件
    :param email_to: 收件人
    :param email: 重置用户邮箱
    :param token: token令牌
    :return:
    """
    project_name = settings.PROJECT_NAME
    subject = f'{project_name} - 重置密码的用户邮箱 {email}'
    with open(Path(settings.EMAIL_TEMPLATES_DIR) / 'reset_password.html') as f:
        template_str = f.read()
    if hasattr(token, 'decode'):
        use_token = token.decode()
    else:
        use_token = token
    server_host = settings.SERVER_HOST
    link = f'{server_host}/reset-password?token={use_token}'  # 密码重置链接
    send_email(
        email_to=email_to,
        subject_template=subject,
        html_template=template_str,
        environment={
            'project_name': settings.PROJECT_NAME,
            'username': email,
            'email': email_to,
            'valid_hours': settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS,
            'link': link,
        },
    )


def send_new_account_email(email_to: str, username: str, password: str) -> None:
    """
    发送新帐户电子邮件
    :param email_to: 收件人
    :param username: 用户名
    :param password: 密码
    :return:
    """
    project_name = settings.PROJECT_NAME
    subject = f'{project_name} - 用户的新帐户 {username}'
    with open(Path(settings.EMAIL_TEMPLATES_DIR) / 'new_account.html') as f:
        template_str = f.read()
    link = settings.SERVER_HOST
    send_email(
        email_to=email_to,
        subject_template=subject,
        html_template=template_str,
        environment={
            'project_name': settings.PROJECT_NAME,
            'username': username,
            'password': password,
            'email': email_to,
            'link': link,
        },
    )


def generate_password_reset_token(email):
    """
    生成密码，重置token令牌
    :param email: 用户邮箱
    :return:
    """
    delta = timedelta(hours=settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS)
    now = datetime.utcnow()
    expires = now + delta
    exp = expires.timestamp()  # 转化为时间戳
    encoded_jwt = jwt.encode(
        {"exp": exp, "nbf": now, "sub": password_reset_jwt_subject, "email": email},
        settings.SECRET_KEY,
        algorithm="HS256",
    )
    return encoded_jwt


def verify_password_reset_token(token) -> Optional[str]:
    """
    密码重置，token验证
    :param token: token令牌
    :return: 返回重置密码的用户邮箱
    """
    try:
        decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
        assert decoded_token["sub"] == password_reset_jwt_subject
        return decoded_token["email"]
    except InvalidTokenError:
        return None
